using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Thrift.Protocol;
using Thrift.Transport;

namespace Thrift.Utility
{
    public class ThriftClient<T> : IDisposable where T : class
    {
        private string serviceName;

        private TTransport transport;

        private T instance;        

        private bool disposed;

        public ThriftClient(string serviceName=null)
        {
            disposed = false;
            Type interfaceType = typeof(T);
            if (string.IsNullOrWhiteSpace(serviceName))
            {
                this.serviceName = interfaceType.FullName;
                if (this.serviceName.LastIndexOf(".") != -1)
                {
                    this.serviceName = this.serviceName.Substring(0, this.serviceName.LastIndexOf("."));
                }
                if (this.serviceName.LastIndexOf("+") != -1)
                {
                    this.serviceName = this.serviceName.Substring(0, this.serviceName.LastIndexOf("+"));
                }
            }
            else
            {
                this.serviceName = serviceName;
            }
            transport = ThriftFactory.BorrowInstance(this.serviceName);
            
            var config = (from c in ConfigHelper.GetServiceConfigs() where c.Name == this.serviceName select c).FirstOrDefault();
            if (config == null)
            {
                throw new ThriftException(string.Format("There Is No Service Named \"{0}\"", serviceName));
            }

            TProtocol protocol = new TBinaryProtocol(transport);
            if (config.ProtocolType.ToLower() == "json")
            {
                protocol = new TJSONProtocol(transport);
            }

            Type type = Type.GetType(config.ServiceType);
            //使用Invoker创建实例，InvokerEmitter抛异常“找不到公开实例构造方法”，可能是内部类获取的类名有点奇怪导致，例如“HelloWorldService.Client”。
            //instance = (T)Invoker.CreateInstance<T>(protocol); 
            instance = (T)Activator.CreateInstance(type, protocol);
            if (config.IsMuiltiple.ToLower() == "true")
            {
                TMultiplexedProtocol mProtocol = new TMultiplexedProtocol(protocol, this.serviceName);
                instance = (T)Activator.CreateInstance(type, mProtocol);
            }
        }

        /// <summary>
        /// 返回client实例否，这个根据实际情况再议
        /// </summary>
        public T Instance
        {
            get { return instance; }
        }

        /// <summary>
        /// 无返回接口调用
        /// </summary>
        /// <typeparam name="Q"></typeparam>
        /// <typeparam name="P"></typeparam>
        /// <param name="methodName"></param>
        /// <param name="request"></param>
        public void Invoke<Q>(string methodName, Q request)
        {
            Invoke<Q, string>(methodName, BuildRequest<Q>(methodName,request));
        }

        /// <summary>
        /// 无参接口调用，服务端一定是有请求参数的，这里的无参是指Service方法无参
        /// </summary>
        /// <typeparam name="P"></typeparam>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public P Invoke<P>(string methodName)
        {
            StandResponse<P> response = Invoke<string, P>(methodName, BuildRequest<string>(methodName,string.Empty));
            if (response != null)
            {
                return response.Data;
            }
            return default(P);
        }

        public P Invoke<Q, P>(string methodName, Q request)
        {
            StandResponse<P> response = Invoke<Q, P>(methodName, BuildRequest<Q>(methodName,request));
            if (response != null)
            {
                return response.Data;
            }
            return default(P);
        }

        private StandResponse<P> Invoke<Q, P>(string methodName, StandRequest<Q> request)
        {
            StandResponse<P> res = null;
            StandResponse<string> response = null;             
            try
            {
                string result = (string)Invoker.MethodInvoke(instance, methodName, SerializeHelper.JsonSerialize3(request));
                response = SerializeHelper.JsonDeserialize3<StandResponse<string>>(result);
            }
            catch (IOException idEx)
            {
                throw new ThriftException(ThriftException.ExceptionType.Timeout, string.Format("请求超时。\r请求服务：{0}\r请求方法：{1}\r请求数据：{2}", serviceName, methodName, SerializeHelper.JsonSerialize3(request)));
            }
            catch (TTransportException transEx)
            {
                throw new ThriftException(ThriftException.ExceptionType.ServerUnkown, string.Format("服务端未处理系统异常。\r请求服务：{0}\r请求方法：{1}\r请求数据：{2}", serviceName, methodName, SerializeHelper.JsonSerialize3(request)));
            }
            catch(Exception ex)
            {
                throw ex;
            }
            if (response != null && response.Code == "-1")
            {
                throw new ThriftException(ThriftException.ExceptionType.InvalidRequestPara, string.Format("请求数据异常。\r请求服务：{0}\r请求方法：{1}\r请求数据：{2}", serviceName, methodName, SerializeHelper.JsonSerialize3(request)));
            }
            if (response != null && response.Code == "-2")
            {
                throw new ThriftException(ThriftException.ExceptionType.ServerCaptured, string.Format("服务端系统异常。\r请求服务：{0}\r请求方法：{1}\r请求数据：{2}", serviceName, methodName, SerializeHelper.JsonSerialize3(request)));
            }                      
            if (response != null)
            {
                res = new StandResponse<P>()
                {
                    Code = response.Code,
                    Desc = response.Desc,
                    Data = SerializeHelper.JsonDeserialize3<P>(response.Data)
                };
            }
            return res;
        }

        private StandRequest<Q> BuildRequest<Q>(string methodName,Q request)
        {
            Func<string> getIP = () =>
                {
                    string ip = string.Empty;
                    IPAddress[] address = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
                    if (address != null)
                    {
                        foreach (IPAddress addr in address)
                        {
                            //过滤IPv6的地址信息
                            if (addr.ToString().Length <= 16 && addr.ToString().Length > 5)
                            {
                                ip = addr.ToString();
                                break;
                            }
                        }
                    }
                    return ip;
                };
            Func<string> getRequestUrl = () =>
            {
                string result;
                try
                {
                    if (HttpContext.Current != null && HttpContext.Current.Request != null)
                    {
                        result = HttpContext.Current.Request.Url.AbsoluteUri;
                    }
                    else if (OperationContext.Current != null && OperationContext.Current.RequestContext != null && OperationContext.Current.RequestContext.RequestMessage != null && OperationContext.Current.RequestContext.RequestMessage.Headers != null)
                    {
                        result = OperationContext.Current.RequestContext.RequestMessage.Headers.To.AbsoluteUri;
                    }
                    else
                    {
                        result = string.Empty;
                    }
                }
                catch
                {
                    result = string.Empty;
                }
                return result;
            };
            Func<string> getUserRequestIP = () =>
            {
                string result;
                try
                {
                    if (HttpContext.Current != null && HttpContext.Current.Request != null)
                    {
                        result = HttpContext.Current.Request.UserHostAddress;
                    }
                    else
                    {
                        if (OperationContext.Current != null)
                        {
                            RemoteEndpointMessageProperty endpointProperty = OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
                            if (endpointProperty != null)
                            {
                                result = endpointProperty.Address;
                                return result;
                            }
                        }
                        result = string.Empty;
                    }
                }
                catch
                {
                    result = string.Empty;
                }
                return result;
            };

            StandRequest<Q> q = new StandRequest<Q>();
            q.HostName = Dns.GetHostName();
            q.IP = getIP();
            q.RequestUrl = getRequestUrl();
            q.UserRequestIP = getUserRequestIP();
            q.RequestTime = DateTime.UtcNow;
            q.TimeStamp=(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds.ToString();
            q.Sign = q.SignTimestamp(q.TimeStamp);
            q.MethodName = methodName;
            q.Data = request;
            return q;
        }

        ~ThriftClient()
        {
            Dispose(false);
        }

        protected void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    ThriftFactory.ReturnInstance(serviceName, transport);
                }
                disposed = true;
            }
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}